/******************************************************************************************************
Copyright (C) 2018 Mestrelab Research S.L. All rights reserved.

This file is part of the Mnova scripting toolkit.

Authorized users of MNova Software may use this file freely, but this file is provided AS IS
with NO WARRANTY OF ANY KIND, INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE.
*******************************************************************************************************/

/*globals Application, Env, MessageBox, MoleculeSearcher, MoleculeSearcherViewer, qsTr, settings, WebUtils, WebUtilsQT */

/*jslint indent: 4, maxerr: 50, todo: true, plusplus: true */

function BMRB_Engine() {
	'use strict';

	//Create a WebUtils instance onto constructor method
	this.revisionWebUtilsQT = 16393;

	//Registry engine options
	this.enabledKey = "Molecule Searcher/Engines/" + BMRB_Engine.ID  + "/Active";

	//Instance attributes to configure the engine
	this.features = [MoleculeSearcher.moleculeFeatureID, MoleculeSearcher.spectraFeatureID];
	this.active = settings.value(this.enabledKey, true);
	this.id = BMRB_Engine.ID;

	//Engine specific configuration
	this.engine = {};
	this.engine.molecule = {};
	this.engine.spectra = {};
	this.engine.apiWeb = "http://webapi.bmrb.wisc.edu/v2/";
	this.engine.ID = BMRB_Engine.ID;

	//Molecule settings
	this.engine.molecule.getMethod = "getMolecule";
	this.engine.molecule.downloadMethod = "getMolecule";
	this.engine.molecule.structureDataManagementMethod = "getElementsFromJson";
	this.engine.molecule.downloadManagementMethod = "getElementsFromText";
	this.engine.molecule.textSearchMethod = "getTextSearch";
	this.engine.molecule.iDArrayElement = "value";
	this.engine.molecule.detailMethod = "getMoleculeDetail";
	this.engine.molecule.imageUrl = "http://www.bmrb.wisc.edu/ftp/pub/bmrb/metabolomics/entry_directories/";
	this.engine.molecule.mappedFields = {
		"ID": "Entry.ID",
		"Molecular Formula": "Chem_comp.Formula",
		"InChI": "Chem_comp.InChI_code",
		"Molecular Weight": "Chem_comp.Formula_weight"
	};

	//Spectra settings
	this.engine.spectra.iDArrayElement = "Experiment.Name";
	this.engine.spectra.experimentUrl = "http://www.bmrb.wisc.edu/metabolomics/mol_summary/zip_entry_directory.php?experiment=";
	this.engine.spectra.importAndDownloadMethod = "importAndDownloadSpectra";
	this.engine.spectra.structureDataManagementMethod = "getElementsFromJsonKey";
	this.engine.spectra.structureSearchMethod = "setDetailFromSpectraIds";
	this.engine.spectra.mappedFields = {
		"Submitted Date": "Entry.Submission_date"
	};

	//GUI options configuration.
	this.engine.optionsSettings = {};

	//The property must match with the UI object name. It must be coherent with definition above
	this.engine.options = {};

	//Error code alloweds if the aExitCodeSilenceFeature is enabled in the controlErrors method
	this.engine.exitCodesToBeSilenced = [203];

	//Control revision of the version in order to use the new library when it makes sense.
	if (Application.version.revision >= this.revisionWebUtilsQT) {
		this.webUtils = new WebUtilsQT();
	} else {
		this.webUtils = new WebUtils();
		if (!this.webUtils.isValid) {
			MessageBox.critical(qsTr("Error initializing WebUtils"));
			return;
		}
	}

	BMRB_Engine.prototype.getMolecule = function (aMoleculeId) {

		var response;

		response = this.webUtils.request(this.engine.molecule.imageUrl + aMoleculeId + "/" + aMoleculeId + ".mol", undefined, "", "", false);

		return response; //this.webUtils.tempFile;
	};

	BMRB_Engine.prototype.getTextSearch = function (text) { //aQuery, aParameters, aMaxHits, aProgressBar) {

		var searchUrl, result;

		searchUrl = this.engine.apiWeb + "instant?term=" + text + "&database=chemcomps";
		result = this.getJSONfromURL(searchUrl);
		result.response = result.dataJson;
		result.dataJson = undefined;

		return result;
	};

	BMRB_Engine.prototype.getEntryExperiments = function (entryCode) {

		var getEntryUrl, response, experimentsJson;

		getEntryUrl = this.engine.apiWeb + "entry/" + entryCode + "?tag=Experiment.ID&tag=Experiment.Raw_data_flag&tag=Experiment.Name&tag=Entry.Submission_date&tag=Experiment_file.Name&tag=Experiment_file.Experiment_ID";
		response = this.getJSONfromURL(getEntryUrl);
		experimentsJson = response.dataJson;

		return experimentsJson[entryCode];
	};

	BMRB_Engine.prototype.getEntryFields = function (entryCode) {

		var getEntryUrl, response, entryJson;

		getEntryUrl = this.engine.apiWeb + "entry/" + entryCode + "?tag=Entry.ID&tag=Chem_comp.Formula&tag=Chem_comp.InChI_code&tag=Chem_comp_descriptor.Type&tag=Chem_comp_descriptor.Descriptor&tag=Chem_comp.Formula_weight&tag=Experiment.Raw_data_flag&tag=Chem_comp.Struct_file_format";
		response = this.getJSONfromURL(getEntryUrl);
		entryJson = response.dataJson;

		return entryJson;
	};

	BMRB_Engine.prototype.getJSONfromURL = function (myurl) {

		var data;

		data = this.webUtils.request(myurl);
		data.dataJson = JSON.parse(data.response);

		return data;
	};

	BMRB_Engine.prototype.getUrlImageFromId = function (moleculeId) {

		return this.engine.molecule.imageUrl + moleculeId + "/" + moleculeId + ".png";

	};

	BMRB_Engine.prototype.getExperimentZipUrl = function (aMoleculeId, aExperimentNumber) {

		return this.engine.spectra.experimentUrl + aExperimentNumber + "&id=" + aMoleculeId;

	};

	BMRB_Engine.prototype.getCompoundSpectraInfo = function (entryId) {

		var i, j,
			response = this.getEntryExperiments(entryId),
			parsedResponse = {
				ExperimentID: [],
				ExperimentName: [],
				ExperimentFileName: [],
				SubmissionDate: response["Entry.Submission_date"]
			};

		for (i = 0; i < response["Experiment.ID"].length; i++) {
			if (response["Experiment.Raw_data_flag"][i] === "yes") {
				parsedResponse.ExperimentID.push(response["Experiment.ID"][i]);
				parsedResponse.ExperimentName.push(response["Experiment.Name"][i]);
				for (j = 0; j < response["Experiment_file.Experiment_ID"].length; j++) {
					if (response["Experiment_file.Experiment_ID"][j] === response["Experiment.ID"][i]) {
						parsedResponse.ExperimentFileName.push(response["Experiment_file.Name"][j]);
						break;
					}
				}
			}
		}

		return {exitCode: undefined, allErrorOutput: undefined, allStrOutput: undefined, response: parsedResponse, isAFile: undefined, url: undefined};
	};

	BMRB_Engine.prototype.importAndDownloadSpectra = function (aID, aFilename) {

		this.webUtils.request(this.getExperimentZipUrl(aID, aFilename), undefined, "", "", true);

		return this.webUtils.tempFile;

	};

	BMRB_Engine.prototype.setDetailFromSpectraIds = function (aProgressBar, aValues, aResponse, aMoleculeId) {

		var i, valueProperties,
			startValue = aProgressBar.value,
			endValue = startValue + 5;

		for (i = 0; i < aResponse.ExperimentID.length; i++) {
			for (valueProperties in aValues) {
				if (aValues.hasOwnProperty(valueProperties)) {
					if (valueProperties === "Database") {
						aValues[valueProperties].push(this.id);
					} else if (valueProperties === "Type") {
						aValues[valueProperties].push(aResponse.ExperimentName[i]);
					} else if (valueProperties === "Submitted Date") {
						aValues[valueProperties].push(aResponse.SubmissionDate[0]);
					} else if (valueProperties === "ID") {
						aValues[valueProperties].push(aMoleculeId);
					} else if (valueProperties === "Filename") {
						aValues[valueProperties].push(aMoleculeId + "_" + aResponse.ExperimentFileName[i] + ".zip");
					} else if (valueProperties === "Comments") {
						aValues[valueProperties].push(aResponse.ExperimentID[i]);
					} else {
						aValues[valueProperties].push("N/A");
					}
				}
			}
			MoleculeSearcherViewer.updateProgressBar(aProgressBar, (i + 1), aResponse.ExperimentID.length, startValue, endValue);
		}
	};

	BMRB_Engine.prototype.getMoleculeDetail = function (aArrayOfIDs, aProgressBar, aValues, aJsonResponse, aMaxEntries, aStatusInfoBar) {

		var i, j, numberOfEntries, maxNumberOfEntries, goodEntries, allResponse, startValue, entryJson, descriptors, descriptorTypes, tableFields, hasSmiles, spectraFlag;

		//Determine the number of rows
		numberOfEntries = aArrayOfIDs.length;
		goodEntries = 0;
		if (aMaxEntries !== undefined && !isNaN(aMaxEntries) && aMaxEntries < numberOfEntries) {
			maxNumberOfEntries = aMaxEntries;
		} else {
			maxNumberOfEntries = numberOfEntries;
		}

		//Initialize vars
		allResponse = aJsonResponse;
		startValue = aProgressBar.value;
		for (i = 0; i < numberOfEntries && !aProgressBar.wasCanceled; i++) {

			hasSmiles = false;

			//Since in the block below we filter by "Chem_comp.Struct_file_format" field we can't show information related to total molecules found (numberOfEntries)
			//The numberOfEntries var is not reliable because we need to filter rows with with empty data in all fields, which does not make sense, i.e. N/A, N/A and so on
			aStatusInfoBar.text = qsTr("Retrieving molecules from {0} engine").format(this.id);

			//Update progress bar
			MoleculeSearcherViewer.updateProgressBar(aProgressBar, (i + 1), numberOfEntries, startValue, 100);

			//Retrieve data by filtering by Struct_file_format to avoid entries with empty data in all fields, which does not make sense, i.e. N/A, N/A and so on
			entryJson = this.getEntryFields(aArrayOfIDs[i])[aArrayOfIDs[i]];
			if (entryJson.hasOwnProperty("Chem_comp.Struct_file_format") && entryJson["Chem_comp.Struct_file_format"].length === 1) {
				for (tableFields in aValues) {
					if (aValues.hasOwnProperty(tableFields)) {
						if (tableFields === "SMILES") {
							if (entryJson.hasOwnProperty("Chem_comp_descriptor.Type") && entryJson.hasOwnProperty("Chem_comp_descriptor.Descriptor")) {
								descriptorTypes = entryJson["Chem_comp_descriptor.Type"];
								descriptors = entryJson["Chem_comp_descriptor.Descriptor"];
								for (j = 0; j < descriptorTypes.length; j++) {
									if (descriptorTypes[j] === "SMILES" && !hasSmiles) {
										aValues.SMILES.push(descriptors[j]);
										hasSmiles = true;
									}
								}
							}
							if (!hasSmiles) {
								aValues.SMILES.push("N/A");
							}
						} else if (tableFields === "Available Spectra") {
							if (entryJson.hasOwnProperty("Experiment.Raw_data_flag") && entryJson["Experiment.Raw_data_flag"].length > 0) {
								spectraFlag = "No";
								for (j = 0; j < entryJson["Experiment.Raw_data_flag"].length; j++) {
									if (entryJson["Experiment.Raw_data_flag"][j] === "yes") {
										spectraFlag = "Yes";
										break;
									}
								}
								aValues["Available Spectra"].push(spectraFlag);
							} else {
								aValues["Available Spectra"].push("No");
							}
						} else if (this.engine.molecule.mappedFields.hasOwnProperty(tableFields) && entryJson.hasOwnProperty(this.engine.molecule.mappedFields[tableFields])) {
							aValues[tableFields].push(entryJson[this.engine.molecule.mappedFields[tableFields]]);
						} else if (tableFields !== "Database" && tableFields !== "") {
							aValues[tableFields].push("N/A");
						}
					}
				}
				++goodEntries;
				aValues.Database.push(BMRB_Engine.ID);
			}
			if (goodEntries > maxNumberOfEntries) {
				break;
			}
		}

		return aValues;

	};

}

//***********Static Attributes***************
BMRB_Engine.ID = "BMRB";

//It sends the engine to the list of available engines
if (Env.MoleculeSearcher_Engines === undefined) {
	Env.MoleculeSearcher_Engines = [];
	MessageBox.critical("Error loading the searcher engines in Molecule Searcher");
}
Env.MoleculeSearcher_Engines.push(BMRB_Engine.ID);